package javango.contrib.hibernate;

import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.LogFactory;

import javango.db.Manager;
import javango.db.ManagerException;
import javango.db.Managers;
import javango.db.QuerySet;

import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;

public class ModelChoiceMap<K> implements Map<K,String> {
	
	Manager<K> manager;
	QuerySet<K> querySet;
	List<K> choiceList;
	boolean addBlankEntry = false;
	Class model;
	
	Map<String,String> choiceMap;
	
	@AssistedInject
	public ModelChoiceMap(Managers models, @Assisted Class model) {
		manager = models.forClass(model);
		this.model = model;
	}
	
	public void clear() {
		throw new UnsupportedOperationException();
	}

	public boolean containsKey(Object key) {
		return get(key) != null;
	}

	public boolean containsValue(Object arg0) {
		throw new UnsupportedOperationException();
	}

	protected QuerySet<K> getFinalQuerySet() throws ManagerException {
		if (querySet == null) {
			return manager.all();
		}
		return querySet;
	}
	
	protected Collection<K> getChoices() throws ManagerException {
		if (choiceList == null) {
			choiceList = getFinalQuerySet().list();
		}
		
		return choiceList;
		
	}
	
	protected Map<String,String> getChoiceMap() {
		if (this.choiceMap != null) return choiceMap;
		
		try {
			// todo this is pretty ugly,  got to be a better way.
			Map<String, String> choices = new LinkedHashMap<String,String>();
	
			if (addBlankEntry) choices.put(null, "--");
			
			String keyField = manager.getPkProperty();
			
			for (K choice : getChoices()) {
				choices.put(BeanUtils.getProperty(choice, keyField), choice.toString());
			}
			
			return choices;
		} catch (Exception e) {
			LogFactory.getLog(ModelChoiceMap.class).error(e);
			return null;
		}

	}
	
	public Set entrySet() {
		return getChoiceMap().entrySet();
	}
	
	public String get(Object key) {
		// TODO Auto-generated method stub
		//  TODO this does not respect filters.
		try {
			if (key == null) {
				return null;
			}
			
			if (key.getClass().equals(model)) {
				Serializable pk = manager.getPk((K)key);
				Object o = manager.get(pk);
				return o == null ? null : o.toString();
			} else {
				Object o = manager.get((Serializable)key);
				return o == null ? null : o.toString();
			}
		} catch (ManagerException e) {
			
			return null;
		}
//		return getChoiceMap().get(key);
	}

	public String put(K key, String value) {
		// TODO Auto-generated method stub
		return null;
	}

	public boolean isEmpty() {
		try {
			return getFinalQuerySet().count() == 0;
		} catch (Exception e) {
			return false;
		}
	}

	public Set<K> keySet() {
		throw new UnsupportedOperationException();
	}

	public String put(Object arg0, Object arg1) {
		throw new UnsupportedOperationException();
	}

	public void putAll(Map arg0) {
		throw new UnsupportedOperationException();
	}

	public String remove(Object arg0) {
		throw new UnsupportedOperationException();
	}

	public int size() {
		try {
		return (int)getFinalQuerySet().count();
		} catch (Exception e) {
			return 0;
		}
	}

	public Collection values() {
		try {
			return getFinalQuerySet().list();
		} catch (Exception e) {
			return null;
		}
	}

	public boolean isAddBlankEntry() {
		return addBlankEntry;
	}

	public ModelChoiceMap<K> setAddBlankEntry(boolean addBlankEntry) {
		this.addBlankEntry = addBlankEntry;
		return this;
	}

	public QuerySet<?> getQuerySet() {
		return querySet;
	}

	public ModelChoiceMap<K> setQuerySet(QuerySet<K> querySet) {
		this.querySet = querySet;
		return this;
	}


}
